using System;
using Sharp3D.Math.Core;
using ReAnimation.Helpers.Common;

namespace GLWrap
{
    public class Camera : ICamera
    {
        private Vector3F _eye;
        private Vector3F _view;
        private Vector3F _up;

        public Vector3F Eye 
        {
            get
            {
                return new Vector3F(_eye);
            }
            set
            {
                _eye = value;
                CameraPositionChanged.Throw(this, EventArgs.Empty);
            }
        }

        public Vector3F View 
        {
            get
            {
                return new Vector3F(_view);
            }
            set
            {
                _view = value;
                CameraPositionChanged.Throw(this, EventArgs.Empty);
            }
        }

        public Vector3F Up 
        {
            get
            {
                return new Vector3F(_up);
            }
            set
            {
                _up = value;
                CameraPositionChanged.Throw(this, EventArgs.Empty);
            }
        }

        public event EventHandler CameraPositionChanged;

        public Camera()
        {
            Eye = new Vector3F(0, 0, 0);
            View = new Vector3F(0, 0, -1);
            Up = new Vector3F(0, 1, 0);
        }

        public Camera(Vector3F eye, Vector3F view, Vector3F up)
        {
            Eye = eye;
            View = view;
            Up = up;
        }

        public void Rotate(float angle, Vector3F vector)
        {
            var temp = new QuaternionF((float) Math.Cos(angle/2.0),vector*(float) Math.Sin(angle/2.0));

            var quatView = new QuaternionF(0,View);

            var result = temp * quatView;

            temp   *= -1;
            temp.W *= -1;

            result *= temp;

            View = new Vector3F(
                result.X,
                result.Y,
                result.Z
                );
            View.Normalize();
        }

        public void Move(Direction direction, float coefficient)
        {
            Vector3F lookVector;
            Vector3F displacementVector;
            lookVector = View - Eye;
            lookVector.Normalize();
            displacementVector = Vector3F.CrossProduct(Up, lookVector);
            displacementVector.Normalize();
            lookVector *= coefficient;
            displacementVector *= coefficient;

            switch (direction)
            {
                case Direction.Up:               
                    View += lookVector;
                    Eye += lookVector;
                    break;
                case Direction.Down:
                    View -= lookVector;
                    Eye -= lookVector;
                    break;
                case Direction.Left:
                    View += displacementVector;
                    Eye += displacementVector;
                    break;
                case Direction.Right:
                    View -= displacementVector;
                    Eye -= displacementVector;
                    break;
                default:
                    throw new ArgumentOutOfRangeException("direction");
            }
        }
    }
}